Skip to content

[PT Run][Time and Date] Add friendly date/time result (#16809)#46803

Open
RealPratham21 wants to merge 1 commit intomicrosoft:mainfrom
RealPratham21:feature/friendly-date-time
Open

[PT Run][Time and Date] Add friendly date/time result (#16809)#46803
RealPratham21 wants to merge 1 commit intomicrosoft:mainfrom
RealPratham21:feature/friendly-date-time

Conversation

@RealPratham21
Copy link
Copy Markdown

Summary of the Pull Request

This PR adds a Friendly date/time result to the Time and Date PT Run plugin.
The plugin now shows a human-readable representation (e.g. “Today”, “Yesterday”, “in 3 hours”, “4 hours ago”) alongside the existing formats when the user queries the current system time or a specific timestamp. The change is localized, documented, and covered by unit tests in the TimeDate plugin test project.

PR Checklist

  • Communication: I've added a comment for this on the issue and the contributors issue as well.
  • Tests: Added unit testing and other test cases to verify correct functionality.
  • Localization: All end-user-facing strings can be localized
  • Dev docs: Added/updated
  • New binaries: Added on the required places
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

  • New Friendly result

    • Added a predefined Friendly result in AvailableResultsList.GetList that is shown together with the existing formats (Time, Date, Now, Unix, ISO 8601, etc.) when returning the full result list.
    • The result value is computed via a new helper TimeAndDateHelper.GetFriendlyDateTime(DateTime target, DateTime referenceNow), which:
      • Uses local date boundaries to return Today, Yesterday, or Tomorrow when appropriate.
      • Otherwise returns relative phrases such as “just now”, “1 minute ago / in 1 minute”, “{N} minutes/hours ago / in {N} minutes/hours”, or “{N} days ago / in {N} days”.
      • Supports both past and future timestamps and respects DateTimeKind (UTC values are converted to local for comparison).
    • The result uses the existing DateTime icon and a new search-tag resource so it can be discovered with terms like “friendly”, “relative”, “yesterday”, “today”, “tomorrow”, or “ago”.
  • Integration and localization

    • AvailableResultsList.GetList gained an optional now parameter so tests can inject a stable reference time; callers in production continue to use current system time when now is not specified.
    • All new user-facing text (Friendly label, Today/Yesterday/Tomorrow, relative phrases and tags) is stored in Resources.resx with corresponding properties in Resources.Designer.cs.
  • Tests and docs

    • Unit tests
      • TimeAndDateHelperTests: added a data-driven test that verifies typical Friendly outputs (e.g. “4 hours ago”, “in 3 hours”, “Today”, “Yesterday”, “Tomorrow”) for a fixed now and different targets.
      • TimeDateResultTests: updated to call the new GetList signature and to ensure existing format values remain unchanged.
      • ImageTests: extended to assert that the Friendly result uses the expected DateTime icon in both dark and light themes.
      • QueryTests: adjusted expected counts for keyword queries that now include the Friendly result, and confirmed that Friendly participates in the same matching logic as other formats.
    • Dev docs
      • doc/devdocs/modules/launcher/plugins/timedate.md: added the Friendly format to the “List of available formats” table with an example like “Yesterday”.

Note on tests: due to tooling constraints on my current machine (Visual Studio workloads / Windows SDK installation blocked by disk space), I was not able to run the TimeDate test project end-to-end locally. The changes are intentionally scoped to the TimeDate plugin and its tests, and I would appreciate CI and at least one local maintainer run of Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests before merge.

Validation Steps Performed

Automated (intended):

  • dotnet test src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj -c Release
    (I could not complete this locally due to missing VS workloads / SDK; please run in CI or on a fully provisioned dev machine.)

Suggested manual validation:

  1. System time query
    • Invoke PT Run and the Time and Date plugin.
    • Type the plugin keyword + now (or just the keyword, depending on configuration).
    • Confirm that:
      • A Friendly result appears alongside existing formats.
      • Its value is a reasonable description of the current time (e.g. “Today”, “just now”).
  2. Relative past/future
    • Query a timestamp a few hours in the past and future (e.g. 10:00 vs an assumed 13:00 now, or specific dates).
    • Confirm Friendly shows “N hours ago” or “in N hours” as expected.
  3. Date boundaries
    • Query dates of “yesterday”, “today”, and “tomorrow” relative to the current date.
    • Confirm the Friendly result reads exactly “Yesterday”, “Today”, or “Tomorrow”.
  4. Search and icons
    • With the plugin keyword, type partial terms like friendly, relative, yesterday.
    • Confirm the Friendly result is returned and uses the same DateTime icon as the existing Date+Time results for both dark and light themes.

@RealPratham21
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Friendly” output to the PT Run Time and Date plugin, intended to display human-readable relative phrases (e.g., “Yesterday”, “in 3 hours”, “4 hours ago”) alongside existing date/time formats.

Changes:

  • Adds new localized strings and search tags for the Friendly format.
  • Introduces TimeAndDateHelper.GetFriendlyDateTime(target, referenceNow) and wires it into AvailableResultsList.GetList(...) via a new optional now parameter for deterministic tests.
  • Updates docs and extends unit tests (result counts, icons, helper tests).

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx Adds Friendly label/phrases and search tags.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs Regenerates strongly-typed accessors for new resources.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs Adds GetFriendlyDateTime helper.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs Adds Friendly result and optional now param for tests.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs Updates calls to new GetList signature.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs Adds data-driven tests for Friendly output.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs Updates expected result counts for keyword searches.
src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs Adds icon assertions for Friendly results (dark/light).
doc/devdocs/modules/launcher/plugins/timedate.md Documents the Friendly format in the formats table.
Files not reviewed (1)
  • src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs: Language not supported

Comment on lines +431 to +445
int dayDiff = (localTarget.Date - localNow.Date).Days;
if (dayDiff == 0)
{
return Resources.Microsoft_plugin_timedate_Friendly_Today;
}

if (dayDiff == -1)
{
return Resources.Microsoft_plugin_timedate_Friendly_Yesterday;
}

if (dayDiff == 1)
{
return Resources.Microsoft_plugin_timedate_Friendly_Tomorrow;
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetFriendlyDateTime returns Today/Yesterday/Tomorrow based on dayDiff before computing the TimeSpan, which makes the relative paths (JustNow, minutes, hours) unreachable for same-day/-1/+1 day timestamps. This contradicts the intended behavior and will fail the newly added unit tests expecting outputs like “4 hours ago” / “in 3 hours”. Consider computing relative seconds/minutes/hours first (for <24h), and only falling back to Today/Yesterday/Tomorrow (and N days) once the difference is large enough / outside the relative range.

Copilot uses AI. Check for mistakes.
<data name="Microsoft_plugin_timedate_SearchTagFriendly" xml:space="preserve">
<value>friendly; relative; ago; yesterday; today; tomorrow</value>
<comment>Don't change order</comment>
</data>
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AvailableResultsList uses ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFriendly"). When isSystemDateTime is true, SelectStringFromResources looks up a ...Now resource key (i.e., Microsoft_plugin_timedate_SearchTagFriendlyNow), but only Microsoft_plugin_timedate_SearchTagFriendly is defined here. This will make the Friendly result not discoverable via its alternative tags (e.g., “relative”, “ago”) when querying system time. Add a Microsoft_plugin_timedate_SearchTagFriendlyNow entry, or pass an explicit stringIdNow so the existing key is used for both cases.

Suggested change
</data>
</data>
<data name="Microsoft_plugin_timedate_SearchTagFriendlyNow" xml:space="preserve">
<value>friendly; relative; ago; yesterday; today; tomorrow; Now</value>
<comment>Don't change order</comment>
</data>

Copilot uses AI. Check for mistakes.
Comment on lines +451 to +476
if (abs.TotalSeconds < 10)
{
return Resources.Microsoft_plugin_timedate_Friendly_JustNow;
}

if (abs.TotalMinutes < 60)
{
int minutes = (int)Math.Round(abs.TotalMinutes, MidpointRounding.AwayFromZero);
if (minutes <= 1)
{
return isFuture ? Resources.Microsoft_plugin_timedate_Friendly_InMinute : Resources.Microsoft_plugin_timedate_Friendly_MinuteAgo;
}

return string.Format(CultureInfo.CurrentCulture, isFuture ? Resources.Microsoft_plugin_timedate_Friendly_InMinutes : Resources.Microsoft_plugin_timedate_Friendly_MinutesAgo, minutes);
}

if (abs.TotalHours < 24)
{
int hours = (int)Math.Round(abs.TotalHours, MidpointRounding.AwayFromZero);
if (hours <= 1)
{
return isFuture ? Resources.Microsoft_plugin_timedate_Friendly_InHour : Resources.Microsoft_plugin_timedate_Friendly_HourAgo;
}

return string.Format(CultureInfo.CurrentCulture, isFuture ? Resources.Microsoft_plugin_timedate_Friendly_InHours : Resources.Microsoft_plugin_timedate_Friendly_HoursAgo, hours);
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The minute/hour/day quantities use Math.Round(..., AwayFromZero) and then special-case <= 1. This can produce incorrect/awkward values near boundaries:

  • 10–29 seconds rounds to 0 minutes, but still returns the singular “in 1 minute” / “1 minute ago” (because minutes <= 1).
  • 59.5 minutes can round to 60 (even though abs.TotalMinutes < 60), yielding “60 minutes ago” instead of “1 hour ago”. Similar risk for hours rounding to 24.
    Consider using floor/ceiling with explicit minimums (e.g., treat <60s as “just now”), and/or clamp boundary values and roll into the next unit when rounding hits 60/24.

Copilot uses AI. Check for mistakes.
{
// Setup
var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(), CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(), now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These GetList(...) calls use a named argument (now: null) followed by positional arguments (CalendarWeekRule.FirstDay, DayOfWeek.Sunday). C# doesn’t allow positional arguments after a named argument, so this won’t compile. Please name the remaining parameters as well (e.g., firstWeekOfYear: ..., firstDayOfWeek: ...) or pass all parameters positionally.

Suggested change
var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(), now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(), now: null, firstWeekOfYear: CalendarWeekRule.FirstDay, firstDayOfWeek: DayOfWeek.Sunday);

Copilot uses AI. Check for mistakes.
{
// Setup
var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(), CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(), now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same compile issue as above: positional arguments are used after the named now: argument. Name the remaining parameters (firstWeekOfYear:, firstDayOfWeek:) or keep all arguments positional.

Suggested change
var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(), now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(), now: null, firstWeekOfYear: CalendarWeekRule.FirstDay, firstDayOfWeek: DayOfWeek.Sunday);

Copilot uses AI. Check for mistakes.
string formatLabel = "Windows file time (Int64 number)";
DateTime timeValue = DateTime.Now;
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same compile issue as above: positional arguments are used after the named now: argument. Name the remaining parameters (firstWeekOfYear:, firstDayOfWeek:) or keep all arguments positional.

Copilot uses AI. Check for mistakes.
string formatLabel = "Era";
DateTime timeValue = DateTime.Now;
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same compile issue as above: positional arguments are used after the named now: argument. Name the remaining parameters (firstWeekOfYear:, firstDayOfWeek:) or keep all arguments positional.

Copilot uses AI. Check for mistakes.
string formatLabel = "Era abbreviation";
DateTime timeValue = DateTime.Now;
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, now: null, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same compile issue as above: positional arguments are used after the named now: argument. Name the remaining parameters (firstWeekOfYear:, firstDayOfWeek:) or keep all arguments positional.

Copilot uses AI. Check for mistakes.
// Setup
DateTime timeValue = new DateTime(2021, 1, 12);
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, weekRule, DayOfWeek.Sunday);
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, now: null, weekRule, DayOfWeek.Sunday);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same compile issue as above: positional arguments are used after the named now: argument. Name the remaining parameters (firstWeekOfYear:, firstDayOfWeek:) or keep all arguments positional.

Copilot uses AI. Check for mistakes.
// Setup
DateTime timeValue = new DateTime(2024, 1, 12); // Friday
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, dayOfWeek);
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, now: null, CalendarWeekRule.FirstDay, dayOfWeek);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same compile issue as above: positional arguments are used after the named now: argument. Name the remaining parameters (firstWeekOfYear:, firstDayOfWeek:) or keep all arguments positional.

Copilot uses AI. Check for mistakes.
@daverayment
Copy link
Copy Markdown
Collaborator

@RealPratham21 Could you please provide some screenshots and/or a video showing you running through the main features of your PR, and which show running some of your manual tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PT Run] [Time and Date plugin] Add friendly date/time format

3 participants